package com.milhouz.bomberrokon.model;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import android.util.Log;

import com.milhouz.bomberrokon.BomberSlashActivity;
import com.milhouz.bomberrokon.sprite.BomberSprite;
import com.milhouz.bomberrokon.sprite.HudSprite;
import com.milhouz.bomberrokon.sprite.SolidObject;
import com.milhouz.bomberrokon.util.TileUtils;

public class MapModel {

	/** Title object type */
	public static final int TILE_EMPTY = 0;
	public static final int TILE_BOMB = 99;

	/** Map info */
	private String mMapCode;
	private String mMapName;
	private int mMaxPlayers;
	private int mOriginMapX;
	private int mOriginMapY;
	private PlayerMapModel[] mPlayersPos;
	private ItemsMapModel mItems;

	/** Map tile textures data */
	private HashMap<Integer, TileData> mTileData;
	private String mExploseWallFile;
	private int mExploseWallNbTiles;
	private String mBackgroundFile = null;
	private String mBackgroundColor = null;

	/** Map tiles */
	private int mNbTileWidth;
	private int mNbTileHeight;
	private int mTileSize;
	private Tile[][] mMapTiles;
	private int mNbDisplayableObj = 0;

	public MapModel(String code) {
		mMapCode = code;
		mTileData = new HashMap<Integer, TileData>();
		mItems = new ItemsMapModel();
	}

	/** Initialize the tile map */
	public void initMapTiles() {
		mMapTiles = new Tile[mNbTileHeight][mNbTileWidth];
		this.calculateMapOrigins();
	}

	/** Count displayable objects into the map */
	public void countNbDisplayableObjects() {
		for (Tile[] row : mMapTiles) {
			for (Tile tile : row) {
				if (tile.getObjectTile() != 0) {
					mNbDisplayableObj++;
				}
			}
		}
	}

	/** Get the tile corresponding to object coordonates (x center,y center) in the screen */
	public Tile getTileOfObject(SolidObject object) {
		int posX = TileUtils.convertSolid2Tile(object.getCenterX(), mTileSize, mOriginMapX);
		int posY = TileUtils.convertSolid2Tile(object.getCenterY(), mTileSize, mOriginMapY);
		// Log.d("BOMBER", "(" + posX + "," + posY + ")");
		if (posX >= 0 && posX < mNbTileWidth && posY >= 0 && posY < mNbTileHeight) {
			return mMapTiles[posY][posX];
		}
		return null;
	}

	/** Get the tile corresponding to object coordonates (x center,y center) in the screen */
	@Deprecated
	public Tile getTileOfObject(int x, int y) {
		int posX = TileUtils.convertSolid2Tile(x, mTileSize, mOriginMapX);
		int posY = TileUtils.convertSolid2Tile(y, mTileSize, mOriginMapY);
		if (posX >= 0 && posX < mNbTileWidth && posY >= 0 && posY < mNbTileHeight) {
			return mMapTiles[posY][posX];
		}
		return null;
	}

	/** Get tiles around the given (x center,y center) coordonate, for the given force */
	public List<Tile> getAroundTiles(float x, float y, int lenghtAround, boolean takeEmpty, boolean takeDestructable, boolean takeIndestructable) {
		int posX = TileUtils.convertSolid2Tile(x, mTileSize, mOriginMapX);
		int posY = TileUtils.convertSolid2Tile(y, mTileSize, mOriginMapY);
		List<Tile> tiles = new ArrayList<Tile>();
		boolean stopUp = false;
		boolean stopDown = false;
		boolean stopLeft = false;
		boolean stopRight = false;
		for (int i = 1; i <= lenghtAround; i++) {
			if (posY + i < mNbTileHeight//
					&& mMapTiles[posY + i][posX] != null //
					&& ((takeEmpty && mMapTiles[posY + i][posX].getObjectTile() == MapModel.TILE_EMPTY) //
							|| (takeIndestructable && !isDestructableTile(posY + i, posX)) //
					|| (takeDestructable && isDestructableTile(posY + i, posX))) //
					&& !stopDown) {
				// down
				if (mMapTiles[posY + i][posX].getObjectTile() != MapModel.TILE_EMPTY || i == lenghtAround) {
					stopDown = true;
					mMapTiles[posY + i][posX].setExplosionBound(true);
				} else {
					mMapTiles[posY + i][posX].setExplosionBound(false);
				}
				if (mMapTiles[posY + i][posX].getObjectTile() != MapModel.TILE_EMPTY //
						&& !isDestructableTile(posY + i, posX)) {
					stopDown = true;
					mMapTiles[posY + i][posX].setExplosionBound(false);
				} else {
					mMapTiles[posY + i][posX].setDestructable(isDestructableTile(posY + i, posX));
					tiles.add(mMapTiles[posY + i][posX]);
				}
			}
			if (posY - i >= 0//
					&& mMapTiles[posY - i][posX] != null //
					&& ((takeEmpty && mMapTiles[posY - i][posX].getObjectTile() == MapModel.TILE_EMPTY) //
							|| (takeIndestructable && !isDestructableTile(posY - i, posX)) //
					|| (takeDestructable && isDestructableTile(posY - i, posX))) //
					&& !stopUp) {
				// up
				if (mMapTiles[posY - i][posX].getObjectTile() != MapModel.TILE_EMPTY || i == lenghtAround) {
					stopUp = true;
					mMapTiles[posY - i][posX].setExplosionBound(true);
				} else {
					mMapTiles[posY - i][posX].setExplosionBound(false);
				}
				if (mMapTiles[posY - i][posX].getObjectTile() != MapModel.TILE_EMPTY //
						&& !isDestructableTile(posY - i, posX)) {
					stopUp = true;
					mMapTiles[posY - i][posX].setExplosionBound(false);
				} else {
					mMapTiles[posY - i][posX].setDestructable(isDestructableTile(posY - i, posX));
					tiles.add(mMapTiles[posY - i][posX]);
				}
			}
			if (posX - i >= 0//
					&& mMapTiles[posY][posX - i] != null //
					&& ((takeEmpty && mMapTiles[posY][posX - i].getObjectTile() == MapModel.TILE_EMPTY) //
							|| (takeIndestructable && !isDestructableTile(posY, posX - i)) //
					|| (takeDestructable && isDestructableTile(posY, posX - i))) //
					&& !stopLeft) {
				// left
				if (mMapTiles[posY][posX - i].getObjectTile() != MapModel.TILE_EMPTY || i == lenghtAround) {
					stopLeft = true;
					mMapTiles[posY][posX - i].setExplosionBound(true);
				} else {
					mMapTiles[posY][posX - i].setExplosionBound(false);
				}
				if (mMapTiles[posY][posX - i].getObjectTile() != MapModel.TILE_EMPTY //
						&& !isDestructableTile(posY, posX - i)) {
					stopLeft = true;
					mMapTiles[posY][posX - i].setExplosionBound(false);
				} else {
					mMapTiles[posY][posX - i].setDestructable(isDestructableTile(posY, posX - i));
					tiles.add(mMapTiles[posY][posX - i]);
				}
			}
			if (posX + i < mNbTileWidth//
					&& mMapTiles[posY][posX + i] != null //
					&& ((takeEmpty && mMapTiles[posY][posX + i].getObjectTile() == MapModel.TILE_EMPTY) //
							|| (takeIndestructable && !isDestructableTile(posY, posX + i)) //
					|| (takeDestructable && isDestructableTile(posY, posX + i))) //
					&& !stopRight) {
				// right
				if (mMapTiles[posY][posX + i].getObjectTile() != MapModel.TILE_EMPTY || i == lenghtAround) {
					stopRight = true;
					mMapTiles[posY][posX + i].setExplosionBound(true);
				} else {
					mMapTiles[posY][posX + i].setExplosionBound(false);
				}
				if (mMapTiles[posY][posX + i].getObjectTile() != MapModel.TILE_EMPTY //
						&& !isDestructableTile(posY, posX + i)) {
					stopRight = true;
					mMapTiles[posY][posX + i].setExplosionBound(false);
				} else {
					mMapTiles[posY][posX + i].setDestructable(isDestructableTile(posY, posX + i));
					tiles.add(mMapTiles[posY][posX + i]);
				}
			}
		}
		return tiles;
	}

	private boolean isDestructableTile(int y, int x) {
		return getMapTileData(mMapTiles[y][x].getObjectTile()) != null && getMapTileData(mMapTiles[y][x].getObjectTile()).isDestructable();
	}

	/**
	 * Return the center, right, left, up and bottom tile around the given bomber sprite
	 * 
	 * @param bomberSprite
	 * @return Return the Return the right, left, up and bottom tile around the given bomber spriteright, left, up and bottom tile around the given
	 *         bomber sprite
	 */
	public Tile[] getAroundTiles(BomberSprite bomberSprite) {
		Tile[] aroundTiles = new Tile[5];
		Tile bomberTile = getTileOfObject(bomberSprite);
		if (bomberTile != null) {
			aroundTiles[0] = bomberTile;
			if (bomberTile.getIndexTileX() - 1 >= 0) { // left tile
				aroundTiles[BomberSprite.DIRECTION_LEFT] = getMapTiles()[bomberTile.getIndexTileY()][bomberTile.getIndexTileX() - 1];
			}
			if (bomberTile.getIndexTileX() + 1 < mNbTileWidth) { // right tile
				aroundTiles[BomberSprite.DIRECTION_RIGHT] = getMapTiles()[bomberTile.getIndexTileY()][bomberTile.getIndexTileX() + 1];
			}
			if (bomberTile.getIndexTileY() - 1 >= 0) { // top tile
				aroundTiles[BomberSprite.DIRECTION_UP] = getMapTiles()[bomberTile.getIndexTileY() - 1][bomberTile.getIndexTileX()];
			}
			if (bomberTile.getIndexTileY() + 1 < mNbTileWidth) { // bottom tile
				aroundTiles[BomberSprite.DIRECTION_DOWN] = getMapTiles()[bomberTile.getIndexTileY() + 1][bomberTile.getIndexTileX()];
			}
		} else {
			Log.e("bomberman", "Bomber tile null, bomber may be out of bounds");
		}
		// Log.e("BOMBER", "tiles around : ");
		// for (int i = 0; i < aroundTiles.length; i++) {
		// if (aroundTiles[i] != null) {
		// Log.e("BOMBER", aroundTiles[i].toString());
		// } else {
		// Log.e("BOMBER", "NULL");
		// }
		// }
		return aroundTiles;
	}

	/** Calculate the origins of the map (centered in the screen) */
	private void calculateMapOrigins() {
		// center map on the center screen X
		mOriginMapX = (BomberSlashActivity.SCREEN_WIDTH - getWidthPixel()) / 2;
		// center map on the center screen Y
		mOriginMapY = HudSprite.HUD_HEIGHT + ((BomberSlashActivity.SCREEN_HEIGHT - HudSprite.HUD_HEIGHT) - getHeightPixel()) / 2;
	}

	/** Get the initial position X center in the screen (in px) of the given player id */
	public int getPlayerCenterX(int index) {
		return TileUtils.convertTile2Solid(getPlayersPos()[index].getInitialPosX(), mTileSize, mOriginMapX);
	}

	/** Get the initial position Y center in the screen (in px) of the given player id */
	public int getPlayerCenterY(int index) {
		return TileUtils.convertTile2Solid(getPlayersPos()[index].getInitialPosY(), mTileSize, mOriginMapY);
	}

	// /////////////////////////
	// Getters / Setters
	// /////////////////////////
	public String getCompleteMapName() {
		return mMapName + " (" + mNbTileWidth + "x" + mNbTileHeight + ")";
	}

	public void setMapName(String mMapName) {
		this.mMapName = mMapName;
	}

	public int getMaxPlayers() {
		return mMaxPlayers;
	}

	public void setMaxPlayers(int mMaxPlayers) {
		this.mMaxPlayers = mMaxPlayers;
		mPlayersPos = new PlayerMapModel[mMaxPlayers];
	}

	public int getNbTileWidth() {
		return mNbTileWidth;
	}

	public void setNbTileWidth(int mNbTileX) {
		mNbTileWidth = mNbTileX;
	}

	public int getNbTileHeight() {
		return mNbTileHeight;
	}

	public void setNbTileHeight(int mNbTileY) {
		mNbTileHeight = mNbTileY;
	}

	public int getTileSize() {
		return mTileSize;
	}

	public void setTileSize(int mTileSize) {
		this.mTileSize = mTileSize;
	}

	public Tile[][] getMapTiles() {
		return mMapTiles;
	}

	public int getNbDisplayableObj() {
		return mNbDisplayableObj;
	}

	public HashMap<Integer, TileData> getTileData() {
		return mTileData;
	}

	public TileData getMapTileData(int tileIndex) {
		return mTileData.get(tileIndex);
	}

	public int getWidthPixel() {
		return mNbTileWidth * mTileSize;
	}

	public int getHeightPixel() {
		return mNbTileHeight * mTileSize;
	}

	public int getOriginMapX() {
		return mOriginMapX;
	}

	public int getOriginMapY() {
		return mOriginMapY;
	}

	/** Get player data array */
	public PlayerMapModel[] getPlayersPos() {
		return mPlayersPos;
	}

	public ItemsMapModel getItems() {
		return mItems;
	}

	public String getMapCode() {
		return mMapCode;
	}

	public void setMapCode(String mapCode) {
		mMapCode = mapCode;
	}

	public String getExploseWallFile() {
		return mExploseWallFile;
	}

	public void setExploseWallFile(String exploseWallFile) {
		mExploseWallFile = exploseWallFile;
	}

	public int getExploseWallNbTiles() {
		return mExploseWallNbTiles;
	}

	public void setExploseWallNbTiles(int exploseWallNbTiles) {
		mExploseWallNbTiles = exploseWallNbTiles;
	}

	public String getBackgroundFile() {
		return mBackgroundFile;
	}

	public void setBackgroundFile(String backgroundFile) {
		mBackgroundFile = backgroundFile;
	}

	public String getBackgroundColor() {
		return mBackgroundColor;
	}

	public void setBackgroundColor(String backgroundColor) {
		mBackgroundColor = backgroundColor;
	}

	@Override
	public String toString() {
		StringBuffer sb = new StringBuffer();
		sb.append("Map : ").append(mMapName).append("\n");
		sb.append(" - Max players = ").append(mMapName).append("\n");
		sb.append(" - Nb tiles X  = ").append(mNbTileWidth).append("\n");
		sb.append(" - Nb tiles Y  = ").append(mNbTileHeight).append("\n");
		sb.append(" - Textures    = ").append(mTileData).append("\n");
		sb.append(" - Tile size   = ").append(mTileSize).append("px\n");
		sb.append(" - Tiles       = \n");
		for (Tile[] row : mMapTiles) {
			for (Tile valueTile : row) {
				sb.append(valueTile.getObjectTile()).append(",");
			}
			sb.append("\n");
		}
		return sb.toString();
	}

}
